﻿using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Permissions;

namespace MIAP.Negocio.Utiles
{
    [SecurityPermission(SecurityAction.Demand, ControlPrincipal = true)]
    public class ImpersonateProcess
    {
        public string Errores;
        #region constantes
        internal const UInt32 Infinite = 0xffffffff;
        private const Int32 Startf_UseStdHandles = 0x00000100;
        private const Int32 StdOutputHandle = -11;
        private const Int32 StdErrorHandle = -12;
        private const Int32 STARTF_USESHOWWINDOW = 0x00000001;
        private const Int32 SW_HIDE = 0;
        #endregion

        #region variables
        readonly string _user;
        readonly string _domain;
        readonly string _password;
        readonly string _command;
        #endregion

        #region Estructuras para impersonalizacion
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct StartupInfo
        {
            public int cb;
            public String reserved;
            public String desktop;
            public String title;
            public int x;
            public int y;
            public int xSize;
            public int ySize;
            public int xCountChars;
            public int yCountChars;
            public int fillAttribute;
            public int flags;
            public UInt16 showWindow;
            public UInt16 reserved2;
            public byte reserved3;
            public IntPtr stdInput;
            public IntPtr stdOutput;
            public IntPtr stdError;
        }
       
        private struct ProcessInformation
        {
            public IntPtr process;
            public IntPtr thread;
            public int processId;
            public int threadId;
        }
        #endregion
       
        #region metodos privados
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool CreateProcessWithLogonW(
            String userName,
            String domain,
            String password,
            UInt32 logonFlags,
            String applicationName,
            String commandLine,
            UInt32 creationFlags,
            UInt32 environment,
            String currentDirectory,
            ref   StartupInfo startupInfo,
            out  ProcessInformation processInformation);

        //metodos si queremos esperar el proceso
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);

        [DllImport("Kernel32.dll", SetLastError = true)]
        public static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);

        [DllImport("Kernel32.dll", SetLastError = true)]
        public static extern IntPtr GetStdHandle(IntPtr handle);

        [DllImport("Kernel32.dll", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr handle);
        #endregion

        /// <summary>
        /// constructor principal de la clase
        /// </summary>
        /// <param name="parametrosComando">parametros que sele enviaran al comando</param> 
        public ImpersonateProcess(string parametrosComando)
        {
            var daoConfiguracion = DataAccessFactory.Instance.CreateConfiguracionDao();

            _command =  daoConfiguracion.GetRutaDtExcec().Descripcion ;
            _command += " " + parametrosComando;
            this._domain = daoConfiguracion.GetNombreDominioImpersonate().Descripcion;           
            this._user = daoConfiguracion.GetNombreWindowsUserServerPkg().Descripcion;
            this._password = daoConfiguracion.GetNombreWindowsPasswordServerPkg().Descripcion;
        }

        /// <summary>
        /// ejecuta el proceso con las credenciales proporcionadas en el archivo de configuración
        /// </summary>
        /// <returns></returns>
        public bool EjecutaProceso()
        {
            var startupInfo = new StartupInfo { reserved = null };
            startupInfo.flags &= Startf_UseStdHandles;
            startupInfo.stdOutput = (IntPtr)StdOutputHandle;
            startupInfo.stdError = (IntPtr)StdErrorHandle;
            //marcamos que queremos que se ejecute el proceso sin mostrar ventana
            startupInfo.flags &= STARTF_USESHOWWINDOW;
            startupInfo.showWindow = SW_HIDE;

            ProcessInformation processInfo;

            var currentDirectory = System.IO.Directory.GetCurrentDirectory();
            var reg = CreateProcessWithLogonW(
                    _user,
                    _domain,
                    _password,
                    (UInt32)1,
                    null,
                    this._command,
                    (UInt32)0,
                    (UInt32)0,
                    currentDirectory,
                    ref startupInfo,
                    out processInfo);

            //si queremos esperar el proceso
              
            //WaitForSingleObject(processInfo.process,10000 );
            UInt32 exitCode = 123456;
            //GetExitCodeProcess(processInfo.process, ref exitCode);            
            var ex = new Win32Exception();
            this.Errores="Commando: "+this._command+" error:"+ ex.Message;
            //cerramos el manejador de proceso, aunque este siga en ejecucion no sotros solamente dejamos de manejarlo
            CloseHandle(processInfo.process);
            CloseHandle(processInfo.thread);
            //regresamos si se pudo ejecutar el proceso o no
            return reg;
        }
    }
}
